變數環境用來描述由你創造的變數撰寫的位置,
還有在記憶體中與其他變數的關係,
當執行以下程式碼:
function b() {
var myVar;
console.log(myVar);
}
function a() {
var myVar = 2;
console.log(myVar);
b();
}
var myVar = 1;
console.log(myVar);
a();
會將全域執行脈絡放進執行堆中:
當執行到 myVar = 1 時, myVar 在記憶體空間中的值變為 1,
每個執行脈絡都有自己的變數環境,
在全域執行脈絡呼叫 function a 時,
會產生自己的執行脈絡接著放進執行堆疊中來執行,
執行到 myVar = 2 時,
function a 執行脈絡中的變數環境 myVar 的值變成 2,如下圖:
在 function a 中最後呼叫 function b 時,
function b 一樣會產生自己的執行脈絡接著放進執行堆疊中執行,
由於執行 function b 的執行脈絡時,
function b 的執行脈絡的變數環境 myVar 沒有賦值過,
function b 中的變數 myVar 還是 undefined:
在 Chrome 開發者工具中輸出 myVar 變數後的結果:
全域變數(不在 function 中宣告的變數)在任何地方都可以存取,
如果在 function 中宣告的變數就只有在 function 中存取的到,
因為每個執行脈絡都有自己的變數環境.
這種結果和變數的作用域(Scope)有關,
將剛才的程式碼稍微修改,在 function b 中這次沒有透過 var 宣告 myVar,但一樣在 function b 中打印出 myVar 的值:
function b() {
console.log(myVar);
}
function a() {
var myVar = 2;
b();
}
var myVar = 1;
a();
你可能會認為 myVar 打印出來會出現沒有定義變數的錯誤,
也可能認為 myVar 打印出來是 2, 因為是在 function a 中呼叫 function b 的,
實際透過 chrome 開發者工具的 console 來查看 myVar 變數:
在 function b 中打印出來的 myVar 變數的值為 1,
為什麼會是全域執行脈絡中 myVar 的值 1?
先透過執行堆疊來看程式是如何執行的,
一開始全域執行脈絡會被加進執行堆疊來執行:
接著呼叫 function a 後, function a 的執行脈絡被放進執行堆疊來執行:
在 functin a 中呼叫 function b 後,
function b 的執行脈絡被放進執行堆疊來執行:
在執行 function b 中打印出 myVar 值的程式時,
function b 的執行脈絡的變數環境找不到 myVar,
但當需要存取 myVar 時,
JavaScript 引擎不會只在 function 自己的執行脈絡的變數環境中尋找 myVar,
之前說過執行脈絡在創造階段時還會產生自己的外部環境:
當執行堆疊在執行 function b 的執行脈絡時,
function b 的執行脈絡中的外部環境會參照到全域執行脈絡 :
function a 的執行脈絡的外部環境也會參照到全域執行脈絡 :
執行脈絡所參考到的外部環境不一定要是執行堆疊中正下方的執行脈絡,
也就是 function b 參考到的外部環境不一定是 function a 的執行脈絡,
這就要看 function b 在程式碼中是在哪個執行脈絡中被宣告,
(白話文是 function b 寫在程式碼中的哪個位置)
JavaScript 引擎注重程式碼是寫在哪個執行脈絡中,
(白話文是 JavaScript 引擎注重程式碼寫在哪)
回到執行 function b 中打印 myVar 的值這行程式碼時,
由於在 function b 的執行脈絡中的變數環境找不到 myVar,
所以會往 function b 的外部環境參照到的全域執行脈絡中的變數環境去找,
在全域執行脈絡中的變數環境裡 myVar 的值是 1,
我們可以再繼續宣告新的 function 與呼叫 function ,
讓執行堆疊中的執行脈絡疊很高,
但每個執行脈絡的外部環境都能參照到對應的執行脈絡(依據 function 在哪個執行脈絡宣告的),
這整條鍊就叫做 Scope Chain: